package soar.test.lambda;

import java.text.SimpleDateFormat;
import java.time.Clock;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class App
{
    public static void main(String[] args)
    {
        new App().run();
    }

    @SuppressWarnings({ "serial" })
    private List<String> names = new ArrayList<String>()
    {
        {
            add("Tom");
            add("Jack");
            add("Rose");
            add("David");
            add("小刚");
            add("大李");
            add("老陈");
        }
    };

    private void run()
    {

        try
        {
            String split = "========================================";
            System.out.println(split);

            array();
            System.out.println(split);

            map();
            System.out.println(split);

            object();
            System.out.println(split);

            pick();
            System.out.println(split);

            performance();
            System.out.println(split);
        }
        catch (Exception e)
        {
            System.err.println(String.format("fail: %s", e));
        }
    }

    private void array()
    {
        String split = "------------------------------";

        System.out.println("array");
        System.out.println(split);

        List<String> names_dup;

        for (String name : names)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("反转");
        System.out.println();
        names_dup = new ArrayList<>(names);
        Collections.reverse(names_dup);
        for (String name : names_dup)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("按名字排序");
        System.out.println();
        names_dup = new ArrayList<>(names);
        // names_dup.sort(String::compareTo);
        Collections.sort(names_dup, String::compareTo);
        for (String name : names_dup)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("按名字逆排序");
        System.out.println();
        names_dup = new ArrayList<>(names);
        names_dup.sort(Collections.reverseOrder(String::compareTo));
        // names_dup.sort((x, y) -> -x.compareTo(y));
        for (String name : names_dup)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("打乱次序");
        System.out.println();
        names_dup = new ArrayList<>(names);
        Collections.shuffle(names_dup);
        for (String name : names_dup)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("Optional处理可能为null的字符串");
        System.out.println();
        String s = null;
        System.out.println(String.format("String(%s) length(%d)", s,
                Optional.ofNullable(s).map(String::length).orElse(-1)));
        Optional.ofNullable(s).ifPresent(System.out::println);

        System.out.println(split);

        System.out.println("byte[] -> String");
        System.out.println();
        byte[] barr = new byte[] { (byte) 0xe5, (byte) 0xa3, (byte) 0xae,
                (byte) 0xe4, (byte) 0xb8, (byte) 0x9c, (byte) 0xe9, (byte) 0xa3,
                (byte) 0x8e };
        String bs = Optional.ofNullable(barr).map(String::new).orElse("");
        System.out.println(bs);

        System.out.println(split);

        System.out.println("大小写转换");
        System.out.println();
        List<String> lower = names.stream().map(name -> name.toLowerCase())
                .collect(Collectors.toList());
        for (String name : lower)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        List<String> lower2 = names.stream().map(String::toLowerCase)
                .collect(Collectors.toList());
        for (String name : lower2)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        List<String> lower3 = names.stream().map((name) -> {
            return "A-" + name.toLowerCase();
        }).collect(Collectors.toList());
        for (String name : lower3)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("生成10个随机整数 [100, 1100)");
        System.out.println();
        Random random = new Random();
        Supplier<Integer> supplier = () -> random.nextInt(1000) + 100;
        List<Integer> ints = Stream.generate(supplier).limit(10)
                .collect(Collectors.toList());
        ints.forEach(System.out::println);

        System.out.println(split);

        System.out.println("生成 { 10^^1, 10^^2, ... 10^^n }");
        System.out.println();
        List<Integer> pow10 = Stream.iterate(10, n -> n * 10).limit(6)
                .collect(Collectors.toList());
        pow10.forEach(System.out::println);
        pow10 = Stream.iterate(1, n -> n + 1).map(n -> (int) Math.pow(10, n))
                .limit(6).collect(Collectors.toList());
        pow10.forEach(System.out::println);

        System.out.println(split);

        System.out.println("去除重复元素");
        System.out.println();
        String[] array = { "a", "b", "c", "b", "x", "a" };
        List<String> orig_list = Arrays.asList(array);
        List<String> list = orig_list.stream().distinct()
                .collect(Collectors.toList());
        System.out.println(String.format("%s -> %s",
                orig_list.stream().collect(Collectors.joining(" ")),
                list.stream().collect(Collectors.joining(" "))));

        System.out.println(split);

        System.out.println("连接");
        System.out.println();
        String join;
        String[] empty = new String[0];
        join = Arrays.asList(empty).stream().collect(Collectors.joining(", "));
        System.out
                .println(String.format("empty: (%s) %d", join, join.length()));
        join = Stream.of(array).collect(Collectors.joining(", "));
        System.out.println(join);

        Integer[] arr = { 1, 2, 3, 4, 5 }; // 必须Integer
        join = Stream.of(arr).map(String::valueOf)
                .collect(Collectors.joining(", "));
        System.out.println(join);
        join = Stream.of(6, 7, 8, 9, 10).map(String::valueOf)
                .collect(Collectors.joining("; "));
        System.out.println(join);

        System.out.println(split);

        System.out.println("Arrays.stream");
        System.out.println();
        Arrays.stream(array).map(String::toUpperCase)
                .forEach(System.out::print);
        System.out.println();

        System.out.println(split);

        System.out.println("Stream.Of");
        System.out.println();
        Stream.of(array).map(String::toUpperCase).forEach(x -> {
            System.out.print(x);
            System.out.print(" ");
        });
        System.out.println();

        System.out.println(split);

        System.out.println("[] -> []");
        System.out.println();
        String[] array2 = Stream.of(array).map(String::toUpperCase)
                .collect(Collectors.toList()).toArray(new String[0]);
        for (String name : array2)
        {
            System.out.print(name + " ");
        }
        System.out.println();

        System.out.println(split);

        System.out.println("List<List> -> List");
        System.out.println();
        List<List<Integer>> ll = new ArrayList<>();
        ll.add(Arrays.asList(1, 2, 3, 4, 5));
        ll.add(Arrays.asList(10, 20, 30, 40, 50));
        ll.add(Arrays.asList(100, 200, 300, 400, 500));
        List<Integer> list_all = ll.stream().reduce(new ArrayList<>(),
                (a, l) -> {
                    a.addAll(l);
                    return a;
                });
        System.out.println(String.format("reduce: %s", list_all.stream()
                .map(String::valueOf).collect(Collectors.joining(", "))));
        System.out.println(
                "Sum: " + list_all.stream().reduce(0, (sum, x) -> sum + x));
        List<Integer> list_flatmap = ll.stream().flatMap(l -> l.stream())
                .collect(Collectors.toList());
        System.out.println(String.format("flatMap: %s", list_flatmap.stream()
                .map(String::valueOf).collect(Collectors.joining(", "))));
        System.out.println();
    }

    private void map()
    {
        String split = "----------------------------------------";

        System.out.println("map");
        System.out.println(split);

        Map<Integer, String> map = new HashMap<>();
        map.put(28, "华为");
        map.put(26, "中兴");
        map.put(22, "小米");
        map.put(13, "魅族");
        map.put(250, "锤子");
        map.forEach((k, v) -> {
            System.out.println(k + " -> " + v);
        });
        System.out.println(map.keySet().stream().map(x -> Integer.toString(x))
                .collect(Collectors.joining(",")));

        System.out.println(split);

        // 过滤
        System.out.println("过滤");
        System.out.println();
        Map<Integer, String> filter = map.entrySet().stream()
                .filter(x -> x.getKey() > 20)
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
        filter.forEach((k, v) -> {
            System.out.println(k + " -> " + v);
        });

        System.out.println(split);

        // keySet排序，生成List
        System.out.println("keySet排序，生成List");
        System.out.println();
        List<Integer> keys_sorted = map.keySet().stream()
                .sorted(Integer::compare)
                // .sorted((x, y) -> Integer.compare(x, y))
                // .sorted()
                .collect(Collectors.toList());
        keys_sorted.forEach((k) -> {
            System.out.println(k + " " + map.get(k));
        });
        String s = keys_sorted.stream().map(x -> x + "=" + map.get(x))
                .collect(Collectors.joining("&"));
        System.out.println(s);

        System.out.println(split);

        // entrySet根据key排序，生成List
        System.out.println("entrySet根据key排序，生成List");
        System.out.println();
        List<Map.Entry<Integer, String>> entry_sorted = map.entrySet().stream()
                .sorted(Comparator.comparingInt(Entry::getKey))
                // .sorted((x, y) -> x.getKey().compareTo(y.getKey()))
                .collect(Collectors.toList());
        entry_sorted.forEach(entry -> {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        });
    }

    class User
    {
        private String name;
        private int job;
        private int age;
        private double salary;
        private Date hire_time;

        public User(String name, int job, int age, double salary,
                Date hire_time)
        {
            super();
            this.name = name;
            this.job = job;
            this.age = age;
            this.salary = salary;
            this.hire_time = hire_time;
        }

        public String getName()
        {
            return name;
        }

        public void setName(String name)
        {
            this.name = name;
        }

        public int getJob()
        {
            return job;
        }

        public void setJob(int job)
        {
            this.job = job;
        }

        public int getAge()
        {
            return age;
        }

        public void setAge(int age)
        {
            this.age = age;
        }

        public double getSalary()
        {
            return salary;
        }

        public void setSalary(double salary)
        {
            this.salary = salary;
        }

        public Date getHire_time()
        {
            return hire_time;
        }

        public void setHire_time(Date hire_time)
        {
            this.hire_time = hire_time;
        }

    }

    private void object() throws Exception
    {
        String split = "----------------------------------------";

        System.out.println("object");
        System.out.println(split);

        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        List<User> users = new ArrayList<>();
        users.add(new User("甲一", 1, 20, 3000.34, fmt.parse("2013-02-17")));
        users.add(new User("乙二", 2, 25, 5000.18, fmt.parse("2011-04-11")));
        users.add(new User("丙三", 1, 20, 3200.17, fmt.parse("2009-06-02")));
        users.add(new User("丁三", 3, 30, 8000.96, fmt.parse("2014-11-26")));
        users.add(new User("戊四", 2, 27, 5500.36, fmt.parse("2008-08-23")));
        users.add(new User("己二", 3, 40, 10000.87, fmt.parse("2015-12-12")));
        users.add(new User("庚一", 4, 45, 22000.39, fmt.parse("2010-03-08")));
        users.add(new User("庚一", 4, 40, 20000.39, fmt.parse("2012-03-08")));
        users.add(new User("辛三", 1, 23, 4700.32, fmt.parse("2009-07-07")));
        users.add(new User("壬一", 4, 23, 4800.32, fmt.parse("2010-07-07")));

        System.out.println("全部记录");
        System.out.println();
        users.forEach(u -> {
            System.out.println(String.format(
                    "name(%s) job(%d) age(%d) salary(%.2f) hire_time(%s)",
                    u.getName(), u.getJob(), u.getAge(), u.getSalary(),
                    fmt.format(u.getHire_time())));
        });

        System.out.println(split);

        System.out.println("job字段列表");
        System.out.println();
        users.stream().map(u -> u.getJob()).collect(Collectors.toSet())
                .forEach(x -> {
                    System.out.println(x);
                });

        System.out.println(split);

        System.out.println("匹配");
        System.out.println();
        System.out.println(String.format("All age >= 20 ? : %s",
                users.stream().allMatch(u -> u.getAge() >= 20)));
        System.out.println(String.format("Any age < 20 ? : %s",
                users.stream().anyMatch(u -> u.getAge() < 20)));
        System.out.println(String.format("None age > 50 ? : %s",
                users.stream().noneMatch(u -> u.getAge() > 50)));

        System.out.println(split);

        System.out.println("寻找");
        System.out.println();
        List<User> find = users.stream().filter(u -> u.getName().contains("三"))
                .collect(Collectors.toList());
        System.out.println("所有姓名包含字符串的记录");
        find.forEach(u -> {
            System.out.println(String.format("name(%s)", u.getName()));
        });
        System.out.println();
        System.out.println("所有工作为1,3的记录");
        List<Integer> job_list = Arrays.asList(1, 3);
        find = users.stream().filter(u -> job_list.contains(u.getJob()))
                .collect(Collectors.toList());
        find.forEach(u -> {
            System.out.println(
                    String.format("name(%s) job(%d)", u.getName(), u.getJob()));
        });
        System.out.println();
        System.out.println("第一个包含字符串的记录");
        Optional<User> find_opt = users.stream()
                .filter(u -> u.getName().contains("三")).findFirst();
        if (find_opt.isPresent())
        {
            System.out.println(
                    String.format("name(%s)", find_opt.get().getName()));
        }

        System.out.println(split);

        System.out.println("按职位分组");
        System.out.println();
        Map<Integer, List<User>> users_by_job = users.stream()
                .collect(Collectors.groupingBy(User::getJob));
        users_by_job.forEach((job, job_users) -> {
            System.out.println("job: " + job);
            job_users.forEach(u -> {
                System.out.println(String.format(
                        "    name(%s) job(%d) age(%d) salary(%.2f)  hire_time(%s)",
                        u.getName(), u.getJob(), u.getAge(), u.getSalary(),
                        fmt.format(u.getHire_time())));
            });
            double sum = job_users.stream()
                    .collect(Collectors.summingDouble(User::getSalary));
            System.out
                    .println(String.format("    count: %d    salary_sum: %.2f",
                            job_users.size(), sum));
        });

        System.out.println(split);

        System.out.println("按职位显示名字列表（合并重名）");
        System.out.println();
        Map<Integer, Set<String>> name_by_job = users.stream()
                .collect(Collectors.groupingBy(User::getJob,
                        Collectors.mapping(User::getName, Collectors.toSet())));
        name_by_job.forEach((job, set) -> {
            System.out.println(String.format("job(%d): %s", job,
                    set.stream().collect(Collectors.joining(" "))));
        });

        System.out.println(split);

        System.out.println("按职位计算薪金总额");
        System.out.println();
        Map<Integer, Double> salary_by_job = users.stream()
                .collect(Collectors.groupingBy(User::getJob,
                        Collectors.summingDouble(User::getSalary)));
        salary_by_job.forEach((job, sum) -> {
            System.out.println(
                    String.format("job(%d): salary_sum(%.2f)", job, sum));
        });

        System.out.println(split);

        System.out.println("按职位分组，选年龄最轻的那个");
        System.out.println();
        users_by_job = users.stream()
                .collect(Collectors.groupingBy(User::getJob));
        List<User> result = new ArrayList<>();
        users_by_job.forEach((job, job_users) -> {
            Optional<User> opt = job_users.stream()
                    .min(Comparator.comparingInt(User::getAge));
            if (opt.isPresent())
            {
                result.add(opt.get());
            }
        });
        result.sort(Comparator.comparing(User::getHire_time).reversed());
        result.forEach(u -> {
            System.out.println(String.format(
                    "    name(%s) job(%d) age(%d) salary(%.2f) hire_time(%s)",
                    u.getName(), u.getJob(), u.getAge(), u.getSalary(),
                    fmt.format(u.getHire_time())));
        });

        System.out.println(split);

        System.out.println("按年龄分组(>=30)");
        System.out.println();
        Map<Boolean, List<User>> users_by_age = users.stream().collect(
                Collectors.partitioningBy(user -> user.getAge() >= 30));
        users_by_age.forEach((GE_30, age_users) -> {
            System.out.println(">=30: " + GE_30);
            age_users.forEach(u -> {
                System.out.println(String.format(
                        "    name(%s) job(%d) age(%d) salary(%.2f)  hire_time(%s)",
                        u.getName(), u.getJob(), u.getAge(), u.getSalary(),
                        fmt.format(u.getHire_time())));
            });
        });
    }

    private void pick() throws Exception
    {
        String split = "----------------------------------------";
        System.out.println("pick");
        System.out.println(split);

        try
        {
            System.out.println("pick 5:");
            List<Integer> pick_total = _pick(5);
            pick_total.forEach(x -> System.out.println("\t" + x));
            System.out.println(split);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        try
        {
            System.out.println("pick 11:");
            List<Integer> pick_total = _pick(11);
            pick_total.forEach(x -> System.out.println("\t" + x));
            System.out.println(split);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        try
        {
            System.out.println("pick 13:");
            List<Integer> pick_total = _pick(13);
            pick_total.forEach(x -> System.out.println("\t" + x));
            System.out.println(split);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        try
        {
            System.out.println("pick 15:");
            List<Integer> pick_total = _pick(15);
            pick_total.forEach(x -> System.out.println("\t" + x));
            System.out.println(split);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private List<Integer> _pick(int question_qt) throws Exception
    {
        Map<Integer, List<Integer>> store_qids = new HashMap<>();
        // Arrays.asList -> java.util.Arrays.ArrayList -> java.util.AbstractList
        // set,add,remove都会抛出UnsupportedOperationException
        store_qids.put(1,
                new ArrayList<>(Arrays.asList(100, 101, 102, 103, 104)));
        store_qids.put(2, new ArrayList<>(Arrays.asList(200, 201, 202)));
        store_qids.put(3,
                new ArrayList<>(Arrays.asList(300, 301, 302, 303, 304)));

        int total_qids = store_qids.entrySet().stream()
                .mapToInt(e -> e.getValue().size()).sum();
        System.out.println(String.format("store_qids(%s)",
                store_qids.entrySet().stream()
                        .map(e -> e.getKey() + ":" + e.getValue().size())
                        .collect(Collectors.joining(", "))));
        if (total_qids < question_qt)
        {
            System.err.println(String.format(
                    "store_qids(%s)=%d < question_qt(%d)",
                    store_qids.entrySet().stream()
                            .map(e -> e.getKey() + ":" + e.getValue().size())
                            .collect(Collectors.joining(", ")),
                    total_qids, question_qt));
            throw new Exception();
        }
        // 实际有效的store_id
        List<Integer> stores = new ArrayList<>(store_qids.keySet());

        Map<Integer, List<Integer>> pick_store = stores.stream()
                .collect(Collectors.toMap(x -> x, x -> new ArrayList<>()));
        Random random = new Random();
        int store_index = 0;
        for (int i = 0; i < question_qt; i++)
        {
            // 顺次选择store_id
            int store_id = stores.get(store_index);

            List<Integer> qids = store_qids.get(store_id);
            int index = random.nextInt(qids.size());
            // 随机选择question_id
            pick_store.get(store_id).add(qids.get(index));
            // 去掉已选的题目
            qids.remove(index);
            if (qids.isEmpty())
            {
                stores.remove(store_index);
                continue;
            }

            store_index++;
            if (store_index == stores.size())
                store_index = 0;
        }
        List<Integer> pick_total = new ArrayList<>();
        pick_store.forEach((x, y) -> {
            pick_total.addAll(y);
        });
        return pick_total;
    }

    private void performance()
    {
        String split = "--------------------------------------------------";

        System.out.println("性能测试");
        System.out.println();
        System.out.println(split);

        long begin_time;
        long cost;
        long count;

        System.out.println("生成随机字符串");
        System.out.println();
        begin_time = Instant.now().toEpochMilli();
        String code = "东风";
        String[] source = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "A", "B", "C", "D", "E", "F", code };
        int num = 5000000; // 少于500万没有明显效率差别
        Random random = new Random();
        List<String> strs = Stream
                .generate(() -> Stream
                        .generate(() -> random.nextInt(source.length))
                        .limit(random.nextInt(10) + 10).map(x -> source[x])
                        .collect(Collectors.joining()))
                .limit(num).collect(Collectors.toList());
        cost = Instant.now().toEpochMilli() - begin_time;
        System.out.println(
                String.format("耗时%,d毫秒，%,d条/秒", cost, num * 1000L / cost));
        // strs.forEach(System.out::println);

        System.out.println(split);

        System.out.println("并行流");
        System.out.println();

        begin_time = Instant.now().toEpochMilli();
        count = strs.stream().filter(x -> x.indexOf(code) != -1).count();
        cost = Instant.now().toEpochMilli() - begin_time;
        System.out.println(String.format("单线程：共%,d条，包含%s: %d条，耗时%,d毫秒", num,
                code, count, cost));

        System.out.println();

        begin_time = Instant.now().toEpochMilli();
        count = strs.parallelStream().filter(x -> x.indexOf(code) != -1)
                .count();
        cost = Instant.now().toEpochMilli() - begin_time;
        System.out.println(String.format("多线程：共%,d条，包含%s: %d条，耗时%,d毫秒", num,
                code, count, cost));

        System.out.println(split);

        System.out.println("字符串按长度分组");
        begin_time = Instant.now().toEpochMilli();
        Map<Integer, List<String>> strs_group = strs.stream()
                .collect(Collectors.groupingBy(s -> s.length()));
        System.out.println(String.format("共%,d条，耗时%,d毫秒", strs.size(),
                Instant.now().toEpochMilli() - begin_time));
        strs_group.forEach((len, list) -> {
            System.out.println(String.format("长度%d: %d条", len, list.size()));
            // if (len > 25)
            // {
            // System.out.println(String.format("%s",
            // list.stream().collect(Collectors.joining(","))));
            // }
        });

        System.out.println();

        System.out.println("合并");
        begin_time = Instant.now().toEpochMilli();
        List<String> strs_2 = strs_group.entrySet().stream()
                .filter(entry -> entry.getKey() >= 10).map(Entry::getValue)
                .reduce(new ArrayList<>(), (s, l) -> {
                    s.addAll(l);
                    return s;
                });
        System.out.println(String.format("共%,d条，耗时%,d毫秒", strs_2.size(),
                Instant.now().toEpochMilli() - begin_time));
    }
}
